Repository navigation
Animate players on the SDK base player anim state - #2244
sunzenshen wants to merge 3 commits into
Conversation
| //----------------------------------------------------------------------------- | ||
| // Purpose: Whether a jumping player has landed. The client draws other players a few tenths of a second behind | ||
| // their networked ground flag, so there the jump lasts until the drawn body reaches the ground. | ||
| //----------------------------------------------------------------------------- | ||
| bool CNEOPlayerAnimState::HasLandedFromJump() | ||
| { | ||
| if ( !( m_pPlayer->GetFlags() & FL_ONGROUND ) ) | ||
| { | ||
| #ifdef CLIENT_DLL | ||
| m_flJumpGroundFlagTime = 0.0f; | ||
| #endif | ||
| return false; | ||
| } | ||
|
|
||
| #ifdef CLIENT_DLL | ||
| if ( m_flJumpGroundFlagTime <= 0.0f ) | ||
| { | ||
| m_flJumpGroundFlagTime = gpGlobals->curtime; | ||
| } | ||
|
|
||
| if ( gpGlobals->curtime - m_flJumpGroundFlagTime > JUMP_LAND_MAX_FLAG_LEAD ) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| const Vector &vecOrigin = m_pPlayer->GetAbsOrigin(); | ||
| trace_t tr; | ||
| UTIL_TraceHull( vecOrigin, vecOrigin - Vector( 0, 0, JUMP_LAND_GROUND_DIST ), | ||
| m_pPlayer->GetPlayerMins(), m_pPlayer->GetPlayerMaxs(), | ||
| MASK_PLAYERSOLID, m_pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); | ||
|
|
||
| return tr.fraction < 1.0f || tr.startsolid; | ||
| #else | ||
| return true; | ||
| #endif // CLIENT_DLL | ||
| } |
There was a problem hiding this comment.
Could you explain what this code is doing? A player's animation determines where their hitboxes are, so it's very important we predict a players animation state correctly. Seeing different logic on the client vs server makes me nervous.
There was a problem hiding this comment.
The reason for this code was an attempt to fix an animation quirk with the jump animation, where in the middle of the jump the walk animation is triggered on the way down.
Even with the side by side comparison and foreknowledge of the transition, I don't easily notice it, so I can revert this change and try to stay consistent with upstream. Then we can address this case in a future PR if people still see the issue. I'll take a look when I get back to my dev setup.
There was a problem hiding this comment.
actually just moving in a direction while landing
There was a problem hiding this comment.
I guess this is subjective but testing with host_timescale 0.25, I prefer the look where the character begins moving their legs in the direction of their movement in mid air.
Move the player animation back onto CBasePlayerAnimState, the 2006 SDK base classic NEOTOKYO is built on, which neo compiles but did not use. CNEOPlayerAnimState replaces CHL2MPPlayerAnimState and takes CMultiPlayerAnimState out of the build (the files stay on disk). The base gives classic's torso blend by speed, constant leg ground speeds, the 3 s face-front timer and its feet rules. The new class adds the run legs only while sprinting, the jump held until a drawn landing, one gesture layer for fire, reload, grenade and detpack events, the lean rule, the NeotokyoRebuild#2108 transitioner reset, and a face-front timer counted from the last squared frame so server and client agree. Client-side animation and the network excludes from NeotokyoRebuild#653 are kept.
Wrap neo_playeranimstate.h includes in #ifdef NEO blocks in the HL2MP player headers, following review feedback. Remove HasLandedFromJump() — jump animation tampering is out of scope for this PR. The jump pose now ends when the player has been in the air at least JUMP_MIN_TIME and FL_ONGROUND is set. Also removes the JUMP_LAND_* constants and m_flJumpGroundFlagTime member that were only used by that function.
ef11d9b to
70d19d2
Compare
AdamTadeusz
left a comment
There was a problem hiding this comment.
I'm not opposed to the attempt to stop the legs from running mid air, I just think we need to be careful about diverging behavior between client and server. Also I'm curious if there is a way to check how far away from the ground a player is without doing a trace, maybe by comparing a player's interpolated position against their networked position or something like that, although I couldn't figure it out in the limited testing I did, but at least it makes sense to me in theory that the update that updates the Ground flag should also have the player origin on the ground, and we can maybe check somehow how the players draw origin compares against that, but again an idea for another PR. IDK, networking is hard


Description
Moves the player animation back to the original system used to drive the original NT player animation (CBasePlayerAnimState).
Implements Cases 1, 2, 3, 4 and 7 (L1, L3, L4) of the animation review: jump pose, torso swap on small movements, standing twist, sprint legs, leg speed on the zero-movement models, ladder legs, feet on a flick.
Review page: exported review page (Dropbox) · live review page (opening it lets you request access with your email or Claude account).
This moves the player animation back onto the SDK base,
CBasePlayerAnimState(shared/base_playeranimstate.cpp), which classic NEOTOKYO is built on and which neo has compiled all along without using it. A newCNEOPlayerAnimState(shared/neo/neo_playeranimstate.cpp) derives from it and replacesCHL2MPPlayerAnimStateand its baseCMultiPlayerAnimState(TF2's anim state, arrived with #653).What the base gives, and what is kept from master
From the base, unchanged: the main leg sequence by activity, the 9-way leg pose scaled by speed over the sequence's ground speed, the upper-body aim layers (idle blended with walk / run / crouch walk by speed), the feet / eyes convergence (720 deg/s, 45 deg fade, 90 deg snap, face-front after 3 s via
mp_facefronttime/mp_feetyawrate), and the server setting the body to the feet yaw.In
CNEOPlayerAnimState:ACT_HOPfrom the jump event until the player lands; on the client, landing is the drawn body reaching the ground ([WONTFIX?] Hold the jump pose until a drawn player lands #2241's hull trace, 0.5 s flag fallback). Waist-deep water ends it, as in master.PlayerAnimEvent_tkeeps the event names the weapons send, so no weapon file changes.Dropped on purpose: master's
TranslateActivityhook through the weapon's activity table. The only table is the knife's, compiled server-only and keyed onACT_MP_*activities master's main activities never matched.Kept from #653 unchanged: client-side animation (
UseClientSideAnimation()), the excluded animation network fields, and the player anim event temp entity. #653 left the earlier neo anim state on this base because of "jittery animations"; that class ran without client-side animation and with the cycle excludes commented out. With #653's networking kept, the per-frame check below finds no cycle jitter atcl_updaterate 20.What this does not change
Aim pitch (case 5, classic's -5 degrees comes from elsewhere), lean timing (case 6, viewmodel code), first-person weapon animations (case 8), the lean interpolation workaround in
c_baseanimating.cpp(#653), ragdolls. Instant 180-degree eye turns: the 90-degree feet step can go to opposite sides on server and client, since which side an exact half turn falls on comes down to float rounding (with the eyes at 0 / 0.4 / 0.7 degrees off a half turn, 6 flips each: this PR 6 of 18 flips, master 5 of 18). Here the twist squares up after 3.6 s on both sides; in master it stays until the next turn. Classic's base has the same rule. L4: after a 60-degree flick while running, this PR's feet catch up faster than classic's (largest twist 16-21 degrees against 27-34, back under 3 degrees in 0.11-0.14 s against 0.17-0.19); the 720 deg/s rule is the same, and whether classic's eyes interpolate more slowly was not checked. L3: classic's legs during the climb itself are not verified (its log has no z, first person only); the table's Idle_lower is its reading at 2D speed 0.How this was compared
The code came first: neo against the base classic is built on (
base_playeranimstate.cpp, the SDK templatesdk_playeranimstate.cpp), and classic'sclient.dllread for names, cvars and constants. Then per-frame animation logs from both games (classic'scl_showanimstate_log, neo's harness log) with the same map (nt_rise_ctg), spot, camera, class and weapon (NSF Assault bot with the MPN onbot_mimic 1, its eyes turned bybot_mimic_yaw_offsetkeys), the same scripted keys, read by the same scripts. Then side-by-side clips of classic, master and this PR. Numbers come from the logs, not from watching. neo builds: Release, master f426c22 and this PR; the Debug checks are named. Built with GCC on Linux; not built on MSVC here (upstream CI).Results against classic and master
Review-page pass (eyes turn, lean, pitch, crouch, two jumps, fire, reload, switch, strafe taps, crouch walk, run, running jump):
nt_rise_ctg): legs while climbingGestures on the local player (fire standing and crouched, reload, knife, frag, smoke, supa7 shot and shell reload, a reload cut by a switch, detpack throw and remote): the same sequences and lengths as master within a frame.
Server and network:
cl_updaterate 20: on every frame the leg cycle advances by exactly rate x frame time, no backward steps (master the same), in the review pass and in bot spectating.Bots (10-12,
ntre_rise_ctg, spectating in-eye and chase atcl_updaterate 20;bot_classrecon, assault, support, VIP; a juggernaut onntre_sentinel_jgr): no living player drew sequence 0 (spawn frames included), no missing sequence besides master's ownReload_ZR68Cfor a VIP, ghost carrier upper body present, no run or crouch-walk legs on a still frame (walk legs on a still frame: 13 frames, master 7), no feet spin on a spectator target switch. Standing twists over 20 degrees held more than 3.5 s: 0 (master 43 frames). Walk / run leg swaps while sprinting: 21 (master 37).Debug client, 10 bots: class and model changes with respawns (#2108), a round restart, a juggernaut, jumps: only the known
neo_root.cpplocalizedWszStrassert at startup, nofixme: animDesired. A bot class change at a round restart crashes the Debug server in master too (CNEOPredictedViewModelMuzzleFlash, outside the anim state).Clips (classic / master / this PR, MP4): case 1 jumps, case 2 strafe taps, case 3 face-front, case 4 sprint (master / this PR; classic cannot be filmed in third person), crouch walk / run / running jump, gestures, lean, L4 flick while running; L3 ladder (master / this PR, camera behind in a dark shaft; no classic ladder clip was recorded).
How to test